/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package domain.model.rms;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import domain.controller.rms.RecordStorePool;
import domain.controller.rms.MObjectController;

/**
 *
 * @author Victorino
 */
public abstract class MObjectRMS {

    final private Hashtable attributes = new Hashtable(0);
    private Integer idIdx;
    private Integer labelIdx;
    private Integer serverIdIdx;

    public MObjectRMS(Integer id) {
        load(id);
    }

    public MObjectRMS(int id) {
        load(id);
    }

    public MObjectRMS() {
        load(-1);
    }

    public void load(Integer id) {
        load(id.intValue());
    }

    public void load(int id) {
        setId(new Integer(-1));
        setLabel("");
        setServerId(new Integer(-1));

        reset();

        if (id != -1) {
            setId(new Integer(id));

            byte[] bt = RecordStorePool.getInstance().getRecord(getLoaderType(), getId());
            unSerialize(bt);
            System.out.println("::loaded " + this.toString());
            bt = null;
        }
    }

    public void delete() {
        if (getId() != null) {
            System.out.println("::delete " + this.toString());
            RecordStorePool.getInstance().deleteRecord(getLoaderType(), getId().intValue());
            MObjectController.removeObjectInCache(this);
        }
    }

    protected void setId(Integer id) {
        idIdx = setAttribute(id, idIdx);
    }

    public Integer getId() {
        return getAttributeInteger(idIdx);
    }

    public void setServerId(Integer sId) {
        serverIdIdx = setAttribute(sId, serverIdIdx);
    }

    public Integer getServerId() {
        return getAttributeInteger(serverIdIdx);
    }

    public void setLabel(String label) {
        labelIdx = setAttribute(label, labelIdx);
    }

    public String getLabel() {
        return getAttributeString(labelIdx);
    }

    public void save() {

        Integer id = getId();
        if (id == null || id.intValue() == -1) {
            id = new Integer(RecordStorePool.getInstance().getNextRecordId(getLoaderType()));
            setId(id);
            System.out.println("::insert " + this.toString());
            RecordStorePool.getInstance().storeRecord(getLoaderType(), serialize());
        } else {
            System.out.println("::update " + this.toString());
            RecordStorePool.getInstance().udpateRecord(getLoaderType(), id.intValue(), serialize());
        }
        MObjectController.addObjectInCache(this);
        System.gc();
    }

    private Enumeration sort() {
        Enumeration en = attributes.keys();
        Vector keysOrder = new Vector();

        if (en.hasMoreElements()) {
            keysOrder.addElement(en.nextElement());
        }
        int index = 1;
        while (en.hasMoreElements()) {

            Integer key = (Integer) en.nextElement();

            int j = index - 1;
            for (; j >= 0; j--) {

                Integer lKey = (Integer) keysOrder.elementAt(j);

                if (lKey.intValue() <= key.intValue()) {
                    break;
                }
                if (!keysOrder.contains(lKey)) {
                    keysOrder.insertElementAt(lKey, j + 1);
                }
            }
            if (!keysOrder.contains(key)) {
                keysOrder.insertElementAt(key, j + 1);
            }
        }
        //System.out.println(keysOrder);

        keysOrder.trimToSize();
        return keysOrder.elements();
    }

    protected byte[] serialize() {

        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        try {
            DataOutputStream dOut = new DataOutputStream(bOut);

            Enumeration en = sort();
            while (en.hasMoreElements()) {
                Object key = en.nextElement();
                Object value = attributes.get(key);

                if (value instanceof Boolean) {
                    dOut.writeBoolean(((Boolean) value).booleanValue());
                } else if (value instanceof Integer) {
                    dOut.writeInt(((Integer) value).intValue());
                } else if (value instanceof String) {
                    dOut.writeUTF(value.toString());
                } else if (value instanceof Date) {
                    dOut.writeLong(((Date) value).getTime());
                } else if (value instanceof Vector) {
                    Vector vec = (Vector) value;
                    int size = vec.size();
                    dOut.writeInt(size);
                    for (int j = 0; j < size; j++) {
                        Integer val = (Integer) vec.elementAt(j);
                        dOut.writeInt(val.intValue());
                    }
                }
            }
            dOut = null;
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        byte[] b = bOut.toByteArray();
        bOut = null;
        return b;
    }

    public void unSerialize(byte[] data) {

        if (data == null) {
            return;
        }
        ByteArrayInputStream bIn = new ByteArrayInputStream(data);
        try {
            DataInputStream dIn = new DataInputStream(bIn);
            Enumeration en = sort();

            while (en.hasMoreElements()) {
                Object key = en.nextElement();
                Object value = attributes.get(key);

                if (value instanceof String) {
                    value = dIn.readUTF();
                } else if (value instanceof Integer) {
                    value = new Integer(dIn.readInt());
                } else if (value instanceof Boolean) {
                    value = new Boolean(dIn.readBoolean());
                } else if (value instanceof Date) {
                    value = new Date(dIn.readLong());
                } else if (value instanceof Vector) {
                    int vecSize = dIn.readInt();
                    Vector vec = new Vector(vecSize);
                    for (int j = 0; j < vecSize; j++) {
                        vec.addElement(new Integer(dIn.readInt()));
                    }
                    value = vec;
                }
                attributes.put(key, value);
            }
            dIn = null;
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        data = null;
    }

    protected Integer setAttribute(String string, Integer index) {
        if (string == null || "null".equals(string)) {
            string = "";
        }
        return setAttributeObject((Object) string, index);
    }

    protected Integer setAttribute(Integer value, Integer index) {
        return setAttributeObject((Object) value, index);
    }

    protected Integer setAttribute(Boolean bool, Integer index) {
        return setAttributeObject((Object) bool, index);
    }

    protected Integer setAttribute(Date date, Integer index) {
        return setAttributeObject((Object) date, index);
    }

    protected Integer setAttribute(Vector vec, Integer index) {
        return setAttributeObject((Object) vec, index);
    }

    private Integer setAttributeObject(Object value, Integer index) {

        if (index == null) {
            index = new Integer(attributes.size());
        }
        if (value != null) {
            attributes.put(index, value);
        }
        return index;
    }

    protected Integer getAttributeInteger(Integer index) {
        Object o = getAttribute(index);
        if (o == null) {
            return new Integer(0);
        }
        return (Integer) o;
    }

    private Object getAttribute(Integer index) {
        return attributes.get(index);
    }

    protected Vector getAttributeVector(Integer index) {
        return (Vector) getAttribute(index);
    }

    protected String getAttributeString(Integer index) {
        String str = (String) getAttribute(index);
        if (str == null || str.equals("null")) {
            str = "";
        }
        return str;
    }

    protected Date getAttrituteDate(Integer index) {
        Object o = getAttribute(index);
        if (o == null) {
            return new Date();
        }
        return (Date) o;
    }

    protected Boolean getAttributeBoolean(Integer index) {
        Object o = getAttribute(index);
        if (o == null) {
            return new Boolean(false);
        }
        return (Boolean) o;
    }

    protected abstract void reset();

    public abstract int getLoaderType();
    
    public String toString() {
        return this.getClass().getName() + "[Id=" + getId() + " - label=" + getLabel() + " - serverId="+getServerId()+"]";
    }
}
